<!DOCTYPE html>


  <html class="dark page-post">


<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>Typescript基础及结合React实践(一) | Poetry&#39;s Blog</title>

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
    <meta name="keywords" content="React,Typescript,">
  

  <meta name="description" content="第一部分 Typescript基础一、typescript 安装npm i typescript -g  全局安装完成后，我们新建一个hello.ts的ts文件  // hello.ts内容let a = &quot;poet&quot;  接下来我们在命令行输入tsc hello.ts来编译这个ts文件，然后会在同级目录生成一个编译好了的hello.js文件  // hello.js内容var = &quot;poet&quot;">
<meta name="keywords" content="React,Typescript">
<meta property="og:type" content="article">
<meta property="og:title" content="Typescript基础及结合React实践(一)">
<meta property="og:url" content="http://blog.poetries.top/2018/12/29/ts-intro-and-use-in-react/index.html">
<meta property="og:site_name" content="Poetry&#39;s Blog">
<meta property="og:description" content="第一部分 Typescript基础一、typescript 安装npm i typescript -g  全局安装完成后，我们新建一个hello.ts的ts文件  // hello.ts内容let a = &quot;poet&quot;  接下来我们在命令行输入tsc hello.ts来编译这个ts文件，然后会在同级目录生成一个编译好了的hello.js文件  // hello.js内容var = &quot;poet&quot;">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/545.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/546.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/547.png">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/1480597-86ad083c5149a72e.png">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/1480597-32fc82d43dadb063.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/548.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/549.png">
<meta property="og:updated_time" content="2020-08-15T04:25:31.938Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Typescript基础及结合React实践(一)">
<meta name="twitter:description" content="第一部分 Typescript基础一、typescript 安装npm i typescript -g  全局安装完成后，我们新建一个hello.ts的ts文件  // hello.ts内容let a = &quot;poet&quot;  接下来我们在命令行输入tsc hello.ts来编译这个ts文件，然后会在同级目录生成一个编译好了的hello.js文件  // hello.js内容var = &quot;poet&quot;">
<meta name="twitter:image" content="https://poetries1.gitee.io/img-repo/2019/10/545.png">

  

  
    <link rel="icon" href="/favicon.ico">
  

  <link href="/css/styles.css?v=c114cbeddx" rel="stylesheet">
<link href="/css/other.css?v=c114cbeddx" rel="stylesheet">


  
    <link rel="stylesheet" href="/css/personal-style.css">
  

  

  
  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?40b1f89aa80f2527b3db779c6898c879";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>


  
  <script type="text/javascript">
	(function(){
	    var bp = document.createElement('script');
	    var curProtocol = window.location.protocol.split(':')[0];
	    if (curProtocol === 'https') {
	        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
	    }
	    else {
	        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
	    }
	    var s = document.getElementsByTagName("script")[0];
	    s.parentNode.insertBefore(bp, s);
	})();
  </script>



  
    <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
  

  <!-- 聊天系统 -->
  
    
   <link type="text/css" rel="stylesheet" href="/renxi/default.css">
   <style>
      #modal {
        position: static !important;
      }
      .filter {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        background: #fe5757;
        animation: colorChange 30s ease-in-out infinite;
        animation-fill-mode: both;
        mix-blend-mode: overlay;
      }
  
      @keyframes colorChange {
        0%, 100% {
            opacity: 0;
        }
        50% {
            opacity: .9;
        }
      }
   </style>
</head>
</html>
<body>
  
  
    <span id="toolbox-mobile" class="toolbox-mobile">导航</span>
  

  <div class="post-header CENTER">
   
  <div class="toolbox">
    <a class="toolbox-entry" href="/">
      <span class="toolbox-entry-text">导航</span>
      <i class="icon-angle-down"></i>
      <i class="icon-home"></i>
    </a>
    <ul class="list-toolbox">
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/archives/"
            rel="noopener noreferrer"
            target="_self"
            >
            博客
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/categories/"
            rel="noopener noreferrer"
            target="_self"
            >
            分类
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/tags/"
            rel="noopener noreferrer"
            target="_self"
            >
            标签
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/search/"
            rel="noopener noreferrer"
            target="_self"
            >
            搜索
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/link/"
            rel="noopener noreferrer"
            target="_self"
            >
            友链
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/about/"
            rel="noopener noreferrer"
            target="_self"
            >
            关于
          </a>
        </li>
      
    </ul>
  </div>


</div>


  <div id="toc" class="toc-article">
    <strong class="toc-title">文章目录<i class="iconfont toc-title" style="display:inline-block;color:#87998d;width:20px;height:20px;">&#xf004b;</i></strong>
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#第一部分-Typescript基础"><span class="toc-text">第一部分 Typescript基础</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#一、typescript-安装"><span class="toc-text">一、typescript 安装</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#二、数据类型"><span class="toc-text">二、数据类型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-布尔类型boolean"><span class="toc-text">2.1 布尔类型boolean</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-数字类型number"><span class="toc-text">2.2 数字类型number</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-字符串类型string"><span class="toc-text">2.3 字符串类型string</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-4-数组-Array"><span class="toc-text">2.4 数组 Array</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-5-元组类型tuple"><span class="toc-text">2.5 元组类型tuple</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-6-枚举类型enum"><span class="toc-text">2.6 枚举类型enum</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-7-任意类型-any"><span class="toc-text">2.7 任意类型 any</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-8-null-undefined类型"><span class="toc-text">2.8 null undefined类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-9-void类型"><span class="toc-text">2.9 void类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-10-never类型"><span class="toc-text">2.10 never类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-11-我们要搞明白any、never、void"><span class="toc-text">2.11 我们要搞明白any、never、void</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#三、函数"><span class="toc-text">三、函数</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-函数定义"><span class="toc-text">3.1 函数定义</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-函数参数处理"><span class="toc-text">3.2 函数参数处理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-函数重载"><span class="toc-text">3.3 函数重载</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#四、类"><span class="toc-text">四、类</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-定义一个类"><span class="toc-text">4.1 定义一个类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-类的继承"><span class="toc-text">4.2 类的继承</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-类的修饰符"><span class="toc-text">4.3 类的修饰符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-4-静态属性、静态方法"><span class="toc-text">4.4 静态属性、静态方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-5-抽象类"><span class="toc-text">4.5 抽象类</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#五、接口"><span class="toc-text">五、接口</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-接口规范对象"><span class="toc-text">5.1 接口规范对象</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-接口规范函数"><span class="toc-text">5.2 接口规范函数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-3-接口规范数组"><span class="toc-text">5.3 接口规范数组</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-4-接口规范类"><span class="toc-text">5.4 接口规范类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-5-接口继承接口"><span class="toc-text">5.5 接口继承接口</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#六、泛型"><span class="toc-text">六、泛型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#6-1-函数的泛型"><span class="toc-text">6.1 函数的泛型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-2-类的泛型"><span class="toc-text">6.2 类的泛型</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#第二部分-结合React实践"><span class="toc-text">第二部分 结合React实践</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#一、环境配置"><span class="toc-text">一、环境配置</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-1-初始化项目"><span class="toc-text">1.1 初始化项目</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1-2-安装依赖"><span class="toc-text">1.2 安装依赖</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-2-1-类型定义文件"><span class="toc-text">1.2.1 类型定义文件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-2-2-相关依赖包"><span class="toc-text">1.2.2 相关依赖包</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1-3-Typescript-config配置"><span class="toc-text">1.3 Typescript config配置</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1-4-webpack配置"><span class="toc-text">1.4 webpack配置</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#二、React组件"><span class="toc-text">二、React组件</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-写一个计数器组件"><span class="toc-text">2.1 写一个计数器组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-结合Redux使用"><span class="toc-text">2.2 结合Redux使用</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-1-基础使用"><span class="toc-text">2.2.1 基础使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-2-合并reducers"><span class="toc-text">2.2.2 合并reducers</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-路由"><span class="toc-text">2.3 路由</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-1-基本用法"><span class="toc-text">2.3.1 基本用法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-2-同步路由到redux"><span class="toc-text">2.3.2 同步路由到redux</span></a></li></ol></li></ol></li></ol></li></ol>
  </div>
  




<div class="content content-post CENTER">
   <!-- canvas 彩带 -->
<canvas id="evanyou" width="1302" height="678" style="position: fixed;width: 100%;height: 100%;top: 0;left:0;z-index:-1;"></canvas>

<article id="post-ts-intro-and-use-in-react" class="article article-type-post" itemprop="blogPost">
  <header class="article-header" style="position:relative;">
    <h1 class="post-title">Typescript基础及结合React实践(一)</h1>

    <div class="article-meta">
      <span>
        <i class="icon-calendar"></i>
        <span>2018.12.29</span>
      </span>

      
        <span class="article-author">
          <i class="icon-user"></i>
          <span>Poetry</span>
        </span>
      

      
  <span class="article-category">
    <i class="icon-list"></i>
    <a class="article-category-link" href="/categories/Front-End/">Front-End</a>
  </span>



      

      
      <i class="fa fa-eye"></i> 
        <span id="busuanzi_container_page_pv">
           &nbsp热度 <span id="busuanzi_value_page_pv">
           <i class="fa fa-spinner fa-spin"></i></span>℃
        </span>
      
      
       
          <span class="post-count">
            <i class="fa fa-file-word-o"></i>&nbsp
            <span>字数统计 10.3k字</span>
          </span>

          <span class="post-count">
            <i class="fa fa-columns"></i>&nbsp
            <span>阅读时长 44分</span>
          </span>
      
      
    </div>

    <i class="iconfont" id="toc-eye" style="display:inline-block;color:#b36619;position:absolute;top:0;right:0;cursor:pointer;
    font-size: 24px;">&#xe61c;</i>

  </header>

  <div class="article-content">
    
      <div id="container">
        <h1 id="第一部分-Typescript基础"><a href="#第一部分-Typescript基础" class="headerlink" title="第一部分 Typescript基础"></a>第一部分 Typescript基础</h1><h2 id="一、typescript-安装"><a href="#一、typescript-安装" class="headerlink" title="一、typescript 安装"></a>一、typescript 安装</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">npm i typescript -g</span><br></pre></td></tr></table></figure>
<blockquote>
<p>全局安装完成后，我们新建一个<code>hello.ts</code>的<code>ts</code>文件</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// hello.ts内容</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="string">"poet"</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>接下来我们在命令行输入<code>tsc hello.ts</code>来编译这个<code>ts</code>文件，然后会在同级目录生成一个编译好了的<code>hello.js</code>文件</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// hello.js内容</span></span><br><span class="line"><span class="keyword">var</span> = <span class="string">"poet"</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>那么我们每次都要输<code>tsc hello.ts</code>命令来编译，这样很麻烦，能否让它自动编译？答案是可以的，使用<code>vscode</code>来开发，需要配置一下<code>vscode</code>就可以。</p>
</blockquote>
<blockquote>
<p>首先我们在命令行执行<code>tsc --init</code>来生成配置文件，然后我们在目录下看到生成了一个<code>tsconfig.json</code>文件</p>
</blockquote>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/545.png" alt></p>
<blockquote>
<p>这个<code>json</code>文件里有很多选项</p>
</blockquote>
<ul>
<li><code>target</code>是选择编译到什么语法</li>
<li><code>module</code>则是模块类型</li>
<li><code>outDir</code>则是输出目录，可以指定这个参数到指定目录</li>
</ul>
<blockquote>
<p>更多细节 <a href="https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/tsconfig.json.html" target="_blank" rel="noopener">https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/tsconfig.json.html</a></p>
</blockquote>
<blockquote>
<p>接下来我们需要开启监控了，在<code>vscode</code>任务栏中</p>
</blockquote>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/546.png" alt></p>
<p><strong>Typescript在线编辑器</strong></p>
<blockquote>
<p>建议使用在线编辑器练习 <a href="http://www.typescriptlang.org/play/index.html" target="_blank" rel="noopener">http://www.typescriptlang.org/play/index.html</a></p>
</blockquote>
<h2 id="二、数据类型"><a href="#二、数据类型" class="headerlink" title="二、数据类型"></a>二、数据类型</h2><blockquote>
<p><code>js</code>是弱类型语言，强弱类语言有什么区别呢？<code>typescript</code>最大的优点就是类型检查，可以帮你检查你定义的类型和赋值的类型。</p>
</blockquote>
<h3 id="2-1-布尔类型boolean"><a href="#2-1-布尔类型boolean" class="headerlink" title="2.1 布尔类型boolean"></a>2.1 布尔类型boolean</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 在js中，定义isFlag为true，为布尔类型boolean</span></span><br><span class="line"><span class="keyword">let</span> isFlag = <span class="literal">true</span>;</span><br><span class="line"><span class="comment">// 但是我们也可以重新给它赋值为字符串</span></span><br><span class="line">isFlag = <span class="string">"hello swr"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在ts中，定义isFlag为true，为布尔类型boolean</span></span><br><span class="line"><span class="comment">// 在变量名后加冒号和类型，如  :boolean</span></span><br><span class="line"><span class="keyword">let</span> isFlag:boolean = <span class="literal">true</span></span><br><span class="line"><span class="comment">// 重新赋值到字符串类型会报错</span></span><br><span class="line">isFlag = <span class="string">"hello swr"</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">// 在java中，一般是这样定义，要写变量名也要写类型名</span></span><br><span class="line"><span class="comment">// int a = 10; </span></span><br><span class="line"><span class="comment">// string name = "poetries"</span></span><br></pre></td></tr></table></figure>
<h3 id="2-2-数字类型number"><a href="#2-2-数字类型number" class="headerlink" title="2.2 数字类型number"></a>2.2 数字类型number</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> age:number = <span class="number">28</span>;</span><br><span class="line">age = <span class="number">29</span>;</span><br></pre></td></tr></table></figure>
<h3 id="2-3-字符串类型string"><a href="#2-3-字符串类型string" class="headerlink" title="2.3 字符串类型string"></a>2.3 字符串类型string</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> name:string = <span class="string">"poetries"</span></span><br><span class="line">name = <span class="string">"iamswr"</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>以上<code>boolean</code>、<code>number</code>、<code>string</code>类型有个共性，就是可以通过<code>typeof</code>来获取到是什么类型，是基本数据类型</p>
</blockquote>
<p>那么复杂的数据类型是怎么处理的呢？</p>
<h3 id="2-4-数组-Array"><a href="#2-4-数组-Array" class="headerlink" title="2.4 数组 Array"></a>2.4 数组 Array</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 数组</span></span><br><span class="line"><span class="comment">// 这是一个字符串数组，只能往里面放字符串，写别的类型会报错</span></span><br><span class="line"><span class="keyword">let</span> persion:string[] = [<span class="string">'poetries'</span>, <span class="string">'jing'</span>]</span><br><span class="line"><span class="comment">// 另一个写法 </span></span><br><span class="line"><span class="keyword">let</span> persions:<span class="built_in">Array</span>&lt;string&gt; = [<span class="string">'poetries'</span>, <span class="string">'jing'</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果数组里放对象呢</span></span><br><span class="line"><span class="keyword">let</span> persionObject:<span class="built_in">Array</span>&lt;object&gt; = [&#123;<span class="attr">name</span>:<span class="string">'poetries'</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;]</span><br><span class="line"><span class="keyword">let</span> persionObjects:object[] = [&#123;<span class="attr">name</span>:<span class="string">'poetries'</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在数组中放string、number、boolean、object</span></span><br><span class="line"><span class="keyword">let</span> arr:<span class="built_in">Array</span>&lt;number|object|string|boolean&gt; = [<span class="number">22</span>, <span class="string">'test'</span>, <span class="literal">true</span>, &#123;<span class="attr">name</span>:<span class="string">'poetries'</span>&#125;]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数组中放什么都可以</span></span><br><span class="line"><span class="keyword">let</span> arrAny:<span class="built_in">Array</span>&lt;any&gt; = [<span class="string">'test'</span>,<span class="number">12</span>,<span class="literal">false</span>]</span><br></pre></td></tr></table></figure>
<h3 id="2-5-元组类型tuple"><a href="#2-5-元组类型tuple" class="headerlink" title="2.5 元组类型tuple"></a>2.5 元组类型tuple</h3><ul>
<li>什么是元组类型？其实元组是数组的一种。</li>
<li>有点类似解构赋值，但是又不完全是解构赋值，比如元组类型必须一一对应上</li>
<li>元组类型是一个不可变的数组，长度、类型是不可变的</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 元组类型tuple</span></span><br><span class="line"><span class="comment">// 什么是元组类型？其实元组是数组的一种</span></span><br><span class="line"><span class="keyword">let</span> per :[string,number,object] = [<span class="string">'poetries'</span>,<span class="number">22</span>,&#123;<span class="attr">love</span>: <span class="string">'coding'</span>&#125;]</span><br></pre></td></tr></table></figure>
<h3 id="2-6-枚举类型enum"><a href="#2-6-枚举类型enum" class="headerlink" title="2.6 枚举类型enum"></a>2.6 枚举类型enum</h3><blockquote>
<p>什么是枚举？枚举有点类似一一列举，一个一个数出来。一般用于值是某几个固定的值</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 枚举类型enum</span></span><br><span class="line"></span><br><span class="line">enum sex &#123;</span><br><span class="line">    BOY=<span class="string">'男孩'</span>,</span><br><span class="line">    GIRL=<span class="string">'女孩'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(sex)</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 转化为es5语法</span></span><br><span class="line"><span class="comment">// 我们顺便看看实现的原理</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> sex;</span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params">sex</span>) </span>&#123;</span><br><span class="line"><span class="comment">// 首先这里是一个自执行函数</span></span><br><span class="line"><span class="comment">// 并且把sex定义为对象，传参进给自执行函数</span></span><br><span class="line"><span class="comment">// 然后给sex对象添加属性并且赋值</span></span><br><span class="line">    sex[<span class="string">"BOY"</span>] = <span class="string">"\u7537\u5B69"</span>;</span><br><span class="line">    sex[<span class="string">"GIRL"</span>] = <span class="string">"\u5973\u5B69"</span>;</span><br><span class="line">&#125;)(sex || (sex = &#123;&#125;));</span><br><span class="line"><span class="built_in">console</span>.log(sex);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>比如我们实际项目中，特别是商城类，订单会存在很多状态流转，那么非常适合用枚举</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">enum orderStatus &#123;</span><br><span class="line">    WAIT_FOR_PAY = <span class="string">"待支付"</span>,</span><br><span class="line">    UNDELIVERED = <span class="string">"完成支付，待发货"</span>,</span><br><span class="line">    DELIVERED = <span class="string">"已发货"</span>,</span><br><span class="line">    COMPLETED = <span class="string">"已确认收货"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>到这里，我们会有一个疑虑，为什么我们不这样写呢？</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> orderStatus2 = &#123;</span><br><span class="line">    WAIT_FOR_PAY : <span class="string">"待支付"</span>,</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果我们直接写对象的键值对方式，是可以在外部修改这个值的，而我们通过<code>enum</code>则不能修改定义好的值了</p>
</blockquote>
<h3 id="2-7-任意类型-any"><a href="#2-7-任意类型-any" class="headerlink" title="2.7 任意类型 any"></a>2.7 任意类型 any</h3><blockquote>
<p><code>any</code>有好处也有坏处，特别是前端，很多时候写类型的时候，几乎分不清楚类型，任意去写，写起来很爽，但是对于后续的重构、迭代等是非常不友好的，会暴露出很多问题，某种程度来说，<code>any</code>类型就是放弃了类型检查了</p>
</blockquote>
<p>比如我们有这样一个场景，就是需要获取某一个dom节点</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> btn = <span class="built_in">document</span>.getElementById(<span class="string">'btn'</span>);</span><br><span class="line">btn.style.color = <span class="string">"blue"</span>;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>此时我们发现在<code>ts</code>中会报错</p>
</blockquote>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/547.png" alt></p>
<ul>
<li>因为我们取这个<code>dom</code>节点，有可能取到，也有可能没取到，当没取到的时候，相当于是<code>null</code>，是没有<code>style</code>这个属性的。</li>
<li>那么我们可以给它添加一个类型为<code>any</code></li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 添加一个any类型，此时就不会报错了，但是也相当于放弃了类型检查了</span></span><br><span class="line"><span class="keyword">let</span> btn:any = <span class="built_in">document</span>.getElementById(<span class="string">'btn'</span>);</span><br><span class="line">btn.style.color = <span class="string">"blue"</span>;</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 可以赋值任何类型的值</span></span><br><span class="line"><span class="comment">// 跟以前我们var let声明的一模一样的</span></span><br><span class="line"><span class="keyword">let</span> person:any = <span class="string">"poetries"</span></span><br><span class="line">person = <span class="number">22</span></span><br></pre></td></tr></table></figure>
<h3 id="2-8-null-undefined类型"><a href="#2-8-null-undefined类型" class="headerlink" title="2.8 null undefined类型"></a>2.8 null undefined类型</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// (string | number | null | undefined) 相当于这几种类型</span></span><br><span class="line"><span class="comment">// 是 string 或 number 或 null 或 undefined</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str:(string | number | <span class="literal">null</span> | <span class="literal">undefined</span>)</span><br><span class="line"></span><br><span class="line">str = <span class="string">'poetries'</span></span><br><span class="line">str = <span class="number">28</span></span><br><span class="line">str = <span class="literal">null</span> </span><br><span class="line">str = <span class="literal">undefined</span></span><br></pre></td></tr></table></figure>
<h3 id="2-9-void类型"><a href="#2-9-void类型" class="headerlink" title="2.9 void类型"></a>2.9 void类型</h3><blockquote>
<p><code>void</code>表示没有任何类型，一般是定义函数没有返回值</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// void 不能再函数里写return</span></span><br><span class="line"><span class="comment">// 怎么理解叫没有返回值呢？此时我们给函数return一个值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">say</span>(<span class="params">name:string</span>):<span class="title">void</span></span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'hello:'</span>, name)</span><br><span class="line">    <span class="comment">// return "ok" 会报错</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">undefined</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="comment">//不会报错</span></span><br><span class="line">&#125;</span><br><span class="line">say(<span class="string">'poetries'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回一个字符串类型</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">say1</span>(<span class="params">name:string</span>):<span class="title">string</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'ok'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-10-never类型"><a href="#2-10-never类型" class="headerlink" title="2.10 never类型"></a>2.10 never类型</h3><blockquote>
<p>这个用得很少，一般是用于抛出异常</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">error</span>(<span class="params">message:string</span>):<span class="title">never</span> </span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(message)</span><br><span class="line">&#125;</span><br><span class="line">error(<span class="string">'errorMsg'</span>)</span><br></pre></td></tr></table></figure>
<h3 id="2-11-我们要搞明白any、never、void"><a href="#2-11-我们要搞明白any、never、void" class="headerlink" title="2.11 我们要搞明白any、never、void"></a>2.11 我们要搞明白any、never、void</h3><ul>
<li><code>any</code>是任意的值</li>
<li><code>void</code>是不能有任何值</li>
<li><code>never</code>永远不会有返回值</li>
</ul>
<blockquote>
<p><code>any</code>比较好理解，就是任何值都可以</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> str:any = <span class="string">"hello poetries"</span></span><br><span class="line">str = <span class="number">28</span></span><br><span class="line">str = <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>void</code>不能有任何值(返回值)</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">say</span>(<span class="params"></span>):<span class="title">void</span> </span>&#123;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>never</code>则不好理解，什么叫永远不会有返回值？</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 除了上面举例的抛出异常以外，我们看一下这个例子</span></span><br><span class="line"><span class="comment">// 这个loop函数，一旦开始执行，就永远不会结束</span></span><br><span class="line"><span class="comment">// 可以看出在while中，是死循环，永远都不会有返回值，包括undefined</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loop</span>(<span class="params"></span>):<span class="title">never</span> </span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"陷入死循环啦"</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">loop()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 包括比如JSON.parse也是使用这种 never | any</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">parse</span>(<span class="params">str:string</span>):(<span class="params">never | any</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">JSON</span>.parse(str)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 首先在正常情况下，我们传一个JSON格式的字符串，是可以正常得到一个JSON对象的</span></span><br><span class="line"><span class="keyword">let</span> json = parse(<span class="string">'&#123;"name":"poetries"&#125;'</span>)</span><br><span class="line"><span class="comment">// 但是有时候，传进去的不一定是JSON格式的字符串，那么就会抛出异常</span></span><br><span class="line"><span class="comment">// 此时就需要never了</span></span><br><span class="line"><span class="keyword">let</span> json = parse(<span class="string">"iamswr"</span>)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>也就是说，当一个函数执行的时候，被抛出异常打断了，导致没有返回值或者该函数是一个死循环，永远没有返回值，这样叫做永远不会有返回值。</p>
</blockquote>
<p>实际开发中，是<code>never</code>和联合类型来一起用，比如</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">say</span>(<span class="params"></span>):(<span class="params">never | string</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">"ok"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="三、函数"><a href="#三、函数" class="headerlink" title="三、函数"></a>三、函数</h2><h3 id="3-1-函数定义"><a href="#3-1-函数定义" class="headerlink" title="3.1 函数定义"></a>3.1 函数定义</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHello</span>(<span class="params">name:string</span>):<span class="title">void</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="3-2-函数参数处理"><a href="#3-2-函数参数处理" class="headerlink" title="3.2 函数参数处理"></a>3.2 函数参数处理</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 函数是这样定义的</span></span><br><span class="line"><span class="comment">// 形参和实参一一对应，完全一样</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHello</span>(<span class="params">name:string,age:number</span>):<span class="title">void</span> </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'hello'</span>, name, age)</span><br><span class="line">&#125;</span><br><span class="line">sayHello(<span class="string">'poetries'</span>,<span class="number">22</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 形参和实参要完全一样，如想不一样，则需要配置可选参数，可选参数放在后面</span></span><br><span class="line"><span class="comment">// 可选参数，用 ？ 处理，只能放在后面</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHelloToYou</span>(<span class="params">name:string,age?:number</span>):<span class="title">void</span> </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'hello'</span>, name, age)</span><br><span class="line">&#125;</span><br><span class="line">sayHelloToYou(<span class="string">'poetries'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 那么如何设置默认参数呢？</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ajax</span>(<span class="params">url:string,method:string = <span class="string">'GET'</span></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(url, method)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 那么如何设置剩余参数呢？可以利用扩展运算符</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">...args:Array&lt;number&gt;</span>):<span class="title">number</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">eval</span>(args.join(<span class="string">"+"</span>))</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> total:number = sum(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>)</span><br><span class="line"><span class="built_in">console</span>.log(total)</span><br></pre></td></tr></table></figure>
<h3 id="3-3-函数重载"><a href="#3-3-函数重载" class="headerlink" title="3.3 函数重载"></a>3.3 函数重载</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 那么如何实现函数重载呢？函数重载是java中非常有名的，在java中函数的重载，是指两个或者两个以上的同名函数，参数的个数和类型不一样</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 比如我们现在有两个同名函数</span></span><br><span class="line"><span class="comment">// function eating(name:string) &#123;</span></span><br><span class="line">    </span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line"><span class="comment">// function eating(name:string,age:number) &#123;</span></span><br><span class="line">    </span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line"><span class="comment">// 那么我想达到一个效果</span></span><br><span class="line"><span class="comment">// 当我传参数name时，执行name:string这个函数</span></span><br><span class="line"><span class="comment">// 当我传参数name和age时，执行name:string,age:number这个函数</span></span><br><span class="line"><span class="comment">// 此时该怎么办？</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 接下来看一下typescript中的函数重载</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 首先声明两个函数名一样的函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">eating</span>(<span class="params">name: string</span>):<span class="title">void</span>;</span></span><br><span class="line"><span class="function"><span class="title">function</span> <span class="title">eating</span>(<span class="params">name: number</span>):<span class="title">void</span>;</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">function</span> <span class="title">eating</span>(<span class="params">name:any</span>): <span class="title">void</span> </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(name)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">eating(<span class="string">"hello poetries"</span>)</span><br><span class="line">eating(<span class="number">22</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在typescript中主要体现是同一个同名函数提供多个函数类型定义，函数实际上就只有一个，就是拥有函数体那个，如果想根据传入值类型的不一样执行不同逻辑，则需要在这个函数里面进行一个类型判断。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 那么这个函数重载有什么作用呢？其实在ts中，函数重载只是用来限制参数的个数和类型，用来检查类型的，而且重载不能拆开几个函数，这一点和java的处理是不一样的，需要注意。</span></span><br></pre></td></tr></table></figure>
<h2 id="四、类"><a href="#四、类" class="headerlink" title="四、类"></a>四、类</h2><h3 id="4-1-定义一个类"><a href="#4-1-定义一个类" class="headerlink" title="4.1 定义一个类"></a>4.1 定义一个类</h3><blockquote>
<p>如何定义一个类？</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ts 写法</span></span><br><span class="line"><span class="comment">// 跟es6非常像 没有太大区别</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Persion</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 这里声明的变量 是实例上的属性</span></span><br><span class="line">    name: string;</span><br><span class="line">    age:number;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(name: string, age: number)&#123;</span><br><span class="line">        <span class="comment">// this.name和this.age 必须先在前面声明好类型</span></span><br><span class="line">        <span class="comment">// name: string</span></span><br><span class="line">        <span class="comment">// age: number</span></span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 原型方法</span></span><br><span class="line">    say():string &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'hello poetries'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> Persion(<span class="string">'poetries'</span>, <span class="number">22</span>)</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 那么转为es5呢？</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Persion = <span class="comment">/** @class */</span> (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">Persion</span>(<span class="params">name, age</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// this.name和this.age 必须先在前面声明好类型</span></span><br><span class="line">        <span class="comment">// name: string</span></span><br><span class="line">        <span class="comment">// age: number</span></span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 原型方法</span></span><br><span class="line">    Persion.prototype.say = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'hello poetries'</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">return</span> Persion;</span><br><span class="line">&#125;());</span><br><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> Persion(<span class="string">'poetries'</span>, <span class="number">22</span>);</span><br></pre></td></tr></table></figure>
<h3 id="4-2-类的继承"><a href="#4-2-类的继承" class="headerlink" title="4.2 类的继承"></a>4.2 类的继承</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 和es6也是差不多</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">    name: string;</span><br><span class="line">    age: number;</span><br><span class="line">    <span class="keyword">constructor</span>(name:string, age: number)&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    say():string&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'hello poetries'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Child</span> <span class="keyword">extends</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">    childName: string;</span><br><span class="line">    <span class="keyword">constructor</span>(name: string,age:number,childName:string) &#123;</span><br><span class="line">        <span class="keyword">super</span>(name,age)</span><br><span class="line">        <span class="keyword">this</span>.childName = childName</span><br><span class="line">    &#125;</span><br><span class="line">    childSay():string &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.childName</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> child = <span class="keyword">new</span> Child(<span class="string">'poetries'</span>, <span class="number">22</span>, <span class="string">'静观流叶'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(child)</span><br></pre></td></tr></table></figure>
<h3 id="4-3-类的修饰符"><a href="#4-3-类的修饰符" class="headerlink" title="4.3 类的修饰符"></a>4.3 类的修饰符</h3><ul>
<li><code>public</code>公开的，可以供自己、子类以及其它类访问</li>
<li><code>protected</code>受保护的，可以供自己、子类访问，但是其他就访问不了</li>
<li><code>private</code>私有的，只有自己访问，而子类、其他都访问不了</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Parents</span> </span>&#123;</span><br><span class="line">    public name:string;</span><br><span class="line">    protected age:number;</span><br><span class="line">    private money:number;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 简写</span></span><br><span class="line">   <span class="comment">// constructor(public name:string,protected age:number,private money:number)</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">constructor</span>(name: string, age:number,money:number) &#123;</span><br><span class="line">       <span class="keyword">this</span>.name = name;</span><br><span class="line">       <span class="keyword">this</span>.age = age;</span><br><span class="line">       <span class="keyword">this</span>.money = money;</span><br><span class="line">   &#125;</span><br><span class="line">   getName():string &#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">this</span>.name</span><br><span class="line">   &#125;</span><br><span class="line">   getAge():number&#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">this</span>.age</span><br><span class="line">   &#125;</span><br><span class="line">   getMoney():number&#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">this</span>.money</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> pare = <span class="keyword">new</span> Parents(<span class="string">'poetries'</span>, <span class="number">22</span>, <span class="number">3000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(pare.name)</span><br><span class="line"><span class="comment">// console.log(pare.age)  报错</span></span><br><span class="line"><span class="comment">// console.log(pare.money) 报错</span></span><br></pre></td></tr></table></figure>
<h3 id="4-4-静态属性、静态方法"><a href="#4-4-静态属性、静态方法" class="headerlink" title="4.4 静态属性、静态方法"></a>4.4 静态属性、静态方法</h3><p>跟<code>es6</code>差不多</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person2</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 类的静态属性</span></span><br><span class="line">    <span class="keyword">static</span> name1 = <span class="string">'poetries'</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 类的静态方法</span></span><br><span class="line">    <span class="keyword">static</span> say() &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'hello poetries'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> per2 = <span class="keyword">new</span> Person2()</span><br><span class="line">Person2.say() <span class="comment">// hello poetries</span></span><br><span class="line"><span class="comment">// per2.say() 报错</span></span><br></pre></td></tr></table></figure>
<h3 id="4-5-抽象类"><a href="#4-5-抽象类" class="headerlink" title="4.5 抽象类"></a>4.5 抽象类</h3><ul>
<li>抽象类和方法，有点类似抽取共性出来，但是又不是具体化，比如说，世界上的动物都需要吃东西，那么会把吃东西这个行为，抽象出来</li>
<li>如果子类继承的是一个抽象类，子类必须实现父类里的抽象方法，不然的话不能实例化，会报错</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">/ 关键字 abstract抽象</span><br><span class="line"><span class="comment">// 定义抽象类</span></span><br><span class="line"></span><br><span class="line">abstract <span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 实际上是使用了public修饰符</span></span><br><span class="line">    <span class="comment">// 如果添加private修饰符会报错</span></span><br><span class="line">    abstract eat():<span class="keyword">void</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 需要注意的是这个Animal是不能实例化的</span></span><br><span class="line"><span class="comment">// let animal = new Animal() // 报错</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// // 抽象类的抽象方法，意思就是，需要在继承这个抽象类的子类中</span></span><br><span class="line"><span class="comment">// 实现这个抽象方法，不然会报错</span></span><br><span class="line"><span class="comment">// 报错，因为在子类中没有实现eat抽象方法</span></span><br><span class="line"><span class="comment">// class Person4 extends Animal&#123;</span></span><br><span class="line"><span class="comment">//     test()&#123;</span></span><br><span class="line"><span class="comment">//         console.log("吃米饭")</span></span><br><span class="line"><span class="comment">//     &#125;</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Dog类继承Animal类后并且实现了抽象方法eat，所以不会报错</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dog</span> <span class="keyword">extends</span> <span class="title">Animal</span></span>&#123;</span><br><span class="line">    eat()&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"吃骨头"</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="五、接口"><a href="#五、接口" class="headerlink" title="五、接口"></a>五、接口</h2><blockquote>
<p>这里的接口，主要是一种规范，规范某些类必须遵守规范，和抽象类有点类似，但是不局限于类，还有属性、函数等</p>
</blockquote>
<h3 id="5-1-接口规范对象"><a href="#5-1-接口规范对象" class="headerlink" title="5.1 接口规范对象"></a>5.1 接口规范对象</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//假设我们需要获取用户信息</span></span><br><span class="line"><span class="comment">// 我们通过这样的方式 规范必须传name和age的值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserInfo</span>(<span class="params">user:&#123;name:string,age:number&#125;</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(user.name,user.age)</span><br><span class="line">&#125;</span><br><span class="line">getUserInfo(&#123;<span class="attr">name</span>: <span class="string">'poetries'</span>, <span class="attr">age</span>: <span class="number">22</span>&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这样看挺完美的， 那么问题就出现了，如果我另外还有一个方法，也是需要这个规范呢？</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserInfo1</span>(<span class="params">user:&#123;name:string,age:number&#125;</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;user.name&#125;</span> <span class="subst">$&#123;user.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getInfo</span>(<span class="params">user:&#123;name:string,age:number&#125;</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;user.name&#125;</span> <span class="subst">$&#123;user.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line">getUserInfo1(&#123;<span class="attr">name</span>:<span class="string">"poetries"</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;)</span><br><span class="line">getInfo(&#123;<span class="attr">name</span>:<span class="string">"poetries"</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以看出，函数getUserInfo和getInfo都遵循同一个规范，那么我们有办法对这个规范复用吗？</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 首先把需要复用的规范，写到接口 关键字interface</span></span><br><span class="line">interface infoInterface &#123;</span><br><span class="line">    name: string,</span><br><span class="line">    age: number;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 然后把这个接口 替换到我们需要复用的地方</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserInfo2</span>(<span class="params">user:infoInterface</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(user.name,user.age)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getInfo2</span>(<span class="params">user:infoInterface</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(user.name,user.age)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">getUserInfo2(&#123;<span class="attr">name</span>:<span class="string">"poetries"</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;)</span><br><span class="line">getInfo2(&#123;<span class="attr">name</span>:<span class="string">"poetries"</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 那么有些参数可传可不传，该怎么处理呢？</span></span><br><span class="line"></span><br><span class="line">interface infoInterface2&#123;</span><br><span class="line">    name: string;</span><br><span class="line">    age: number;</span><br><span class="line">    city?:string;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserInfo3</span>(<span class="params">user:infoInterface2</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;user.name&#125;</span> <span class="subst">$&#123;user.age&#125;</span> <span class="subst">$&#123;user.city&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getInfo3</span>(<span class="params">user:infoInterface</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;user.name&#125;</span> <span class="subst">$&#123;user.age&#125;</span>`</span>)</span><br><span class="line">&#125;</span><br><span class="line">getUserInfo3(&#123;<span class="attr">name</span>:<span class="string">"poetries"</span>,<span class="attr">age</span>:<span class="number">22</span>,<span class="attr">city</span>:<span class="string">"深圳"</span>&#125;)</span><br><span class="line">getInfo3(&#123;<span class="attr">name</span>:<span class="string">"iamswr"</span>,<span class="attr">age</span>:<span class="number">22</span>&#125;)</span><br></pre></td></tr></table></figure>
<h3 id="5-2-接口规范函数"><a href="#5-2-接口规范函数" class="headerlink" title="5.2 接口规范函数"></a>5.2 接口规范函数</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 对一个函数的参数和返回值进行规范</span></span><br><span class="line">interface mytotal &#123;</span><br><span class="line">    <span class="comment">// 左侧是函数的参数，右侧是函数的返回类型</span></span><br><span class="line">    (a:number,<span class="attr">b</span>:number):number;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> totalSum:mytotal = <span class="function"><span class="keyword">function</span>(<span class="params">a:number,b:number</span>):<span class="title">number</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(totalSum(<span class="number">10</span>, <span class="number">20</span>))</span><br></pre></td></tr></table></figure>
<h3 id="5-3-接口规范数组"><a href="#5-3-接口规范数组" class="headerlink" title="5.3 接口规范数组"></a>5.3 接口规范数组</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">interface userInterface &#123;</span><br><span class="line">    <span class="comment">// index为数组索引 类型是number</span></span><br><span class="line">    <span class="comment">// 右边是数组里为字符串的数组成员</span></span><br><span class="line">    [index: number]: string;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> arrTest: userInterface = [<span class="string">'poetries'</span>, <span class="string">'静观流叶'</span>]</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(arrTest)</span><br></pre></td></tr></table></figure>
<h3 id="5-4-接口规范类"><a href="#5-4-接口规范类" class="headerlink" title="5.4 接口规范类"></a>5.4 接口规范类</h3><blockquote>
<p>这个比较重要，因为写<code>react</code>的时候会经常使用到类</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 首先实现一个接口</span></span><br><span class="line">interface Animal2 &#123;</span><br><span class="line">    <span class="comment">// 这个类必须有name</span></span><br><span class="line">    name:string;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 这个类必须有eat方法</span></span><br><span class="line">    eat(any:string):<span class="keyword">void</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关键字implements实现</span></span><br><span class="line"><span class="comment">// 因为接口是抽象的，需要通过子类是实现它</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person6</span> <span class="title">implements</span> <span class="title">Animal2</span> </span>&#123;</span><br><span class="line">    name: string;</span><br><span class="line">    <span class="keyword">constructor</span>(name: string) &#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    eat(any:string):<span class="keyword">void</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">`吃`</span>+any)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果想遵循多个接口</span></span><br><span class="line"></span><br><span class="line">interface Animal3 &#123;</span><br><span class="line">    name: string;</span><br><span class="line">    eat(any: string):<span class="keyword">void</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 新增一个接口</span></span><br><span class="line">interface Animal4 &#123;</span><br><span class="line">    sleep():<span class="keyword">void</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 可以在implements后面通过逗号添加和java一样</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person7</span> <span class="title">implements</span> <span class="title">Animal3</span>,<span class="title">Animal4</span> </span>&#123;</span><br><span class="line">    name: string;</span><br><span class="line">    <span class="keyword">constructor</span>(name:string)&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    eat(any:string) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">`吃`</span>+any)</span><br><span class="line">    &#125;</span><br><span class="line">    sleep() &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'睡觉'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="5-5-接口继承接口"><a href="#5-5-接口继承接口" class="headerlink" title="5.5 接口继承接口"></a>5.5 接口继承接口</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">interface Animal5&#123;</span><br><span class="line">    name:string;</span><br><span class="line">    eat(any:string):<span class="keyword">void</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 像类一样 通过extends继承</span></span><br><span class="line">interface Animal6 extends Animal5 &#123;</span><br><span class="line">    sleep():<span class="keyword">void</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 因为Animal6类继承了Animal5</span></span><br><span class="line"><span class="comment">// 所以这里遵循Animal6就相当于把Animal5也继承了</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person8</span> <span class="title">implements</span> <span class="title">Animal2</span> </span>&#123;</span><br><span class="line">    name: string;</span><br><span class="line">    <span class="keyword">constructor</span>(name:string) &#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    eat(any:string):<span class="keyword">void</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">`吃<span class="subst">$&#123;any&#125;</span>`</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    sleep()&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'睡觉'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="六、泛型"><a href="#六、泛型" class="headerlink" title="六、泛型"></a>六、泛型</h2><h3 id="6-1-函数的泛型"><a href="#6-1-函数的泛型" class="headerlink" title="6.1 函数的泛型"></a>6.1 函数的泛型</h3><blockquote>
<p>泛型可以支持不特定的数据类型，什么叫不特定呢？比如我们有一个方法，里面接收参数，但是参数类型我们是不知道，但是这个类型在方法里面很多地方会用到，参数和返回值要保持一致性</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 假设我们有一个需求，我们不知道函数接收什么类型的参数，也不知道返回值的类型</span></span><br><span class="line"><span class="comment">// 而我们又需要传进去的参数类型和返回值的类型保持一致，那么我们就需要用到泛型</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// &lt;T&gt;的意思是泛型，即generic type</span></span><br><span class="line"><span class="comment">// 可以看出value的类型也为T，返回值的类型也为T</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">deal</span>&lt;<span class="title">T</span>&gt;(<span class="params">value:T</span>):<span class="title">T</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> value</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 下面的&lt;string&gt;、&lt;number&gt;实际上用的时候再传给上面的&lt;T&gt;</span></span><br><span class="line"><span class="built_in">console</span>.log(deal&lt;string&gt;(<span class="string">"poetries"</span>))</span><br><span class="line"><span class="built_in">console</span>.log(deal&lt;number&gt;(<span class="number">22</span>))</span><br></pre></td></tr></table></figure>
<blockquote>
<p>实际上，泛型用得还是比较少，主要是看类的泛型是如何使用的</p>
</blockquote>
<h3 id="6-2-类的泛型"><a href="#6-2-类的泛型" class="headerlink" title="6.2 类的泛型"></a>6.2 类的泛型</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyMath</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">// 定义一个私有属性</span></span><br><span class="line"></span><br><span class="line">    private arr: T[] = []</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 规定传参类型</span></span><br><span class="line">    add(value: T) &#123;</span><br><span class="line">        <span class="keyword">this</span>.arr.push(value)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 这里规定了类型为number</span></span><br><span class="line"><span class="comment">// 相当于把T替换为number</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> mymath = <span class="keyword">new</span> MyMath&lt;number&gt;()</span><br><span class="line">mymath.add(<span class="number">1</span>)</span><br><span class="line">mymath.add(<span class="number">2</span>)</span><br><span class="line">mymath.add(<span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<p><strong>有了接口为什么还需要抽象类？</strong></p>
<blockquote>
<p>接口里面只能放定义，抽象类里面可以放普通类、普通类的方法、定义抽象的东西。</p>
</blockquote>
<h1 id="第二部分-结合React实践"><a href="#第二部分-结合React实践" class="headerlink" title="第二部分 结合React实践"></a>第二部分 结合React实践</h1><h2 id="一、环境配置"><a href="#一、环境配置" class="headerlink" title="一、环境配置"></a>一、环境配置</h2><h3 id="1-1-初始化项目"><a href="#1-1-初始化项目" class="headerlink" title="1.1 初始化项目"></a>1.1 初始化项目</h3><ul>
<li>生成一个目录<code>ts_react_demo</code>，输入<code>npm init -y</code>初始化项目</li>
<li>然后在项目里我们需要一个<code>.gitignore</code>来忽略指定目录不传到<code>git</code>上</li>
<li>进入<code>.gitignore</code>输入我们需要忽略的目录，一般是<code>node_modules</code></li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// .gitignore</span><br><span class="line">node_modules</span><br></pre></td></tr></table></figure>
<h3 id="1-2-安装依赖"><a href="#1-2-安装依赖" class="headerlink" title="1.2 安装依赖"></a>1.2 安装依赖</h3><blockquote>
<p>接下来我们准备下载相应的依赖包，这里需要了解一个概念，就是类型定义文件</p>
</blockquote>
<h4 id="1-2-1-类型定义文件"><a href="#1-2-1-类型定义文件" class="headerlink" title="1.2.1 类型定义文件"></a>1.2.1 类型定义文件</h4><blockquote>
<p>因为目前主流的第三方库都是以<code>javascript</code>编写的，如果用<code>typescript</code>开发，会导致在编译的时候会出现很多找不到类型的提示，那么如果让这些库也能在<code>ts</code>中使用呢？</p>
</blockquote>
<ul>
<li>类型定义文件(<code>*.d.ts</code>)就是能够让编辑器或者插件来检测到第三方库中<code>js</code>的静态类型，这个文件是以<code>.d.ts</code>结尾- 比如说<code>react</code>的类型定义文件：<a href="https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react" target="_blank" rel="noopener">https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react</a></li>
<li>在<code>typescript2.0</code>中，是使用<code>@type</code>来进行类型定义，当我们使用<code>@type</code>进行类型定义，<code>typescript</code>会默认查看<code>./node_modules/@types</code>文件夹，可以通过这样来安装这个库的定义库<code>npm install @types/react --save</code></li>
</ul>
<h4 id="1-2-2-相关依赖包"><a href="#1-2-2-相关依赖包" class="headerlink" title="1.2.2 相关依赖包"></a>1.2.2 相关依赖包</h4><p><strong>React相关</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- react // react的核心文件</span><br><span class="line">- @types/react // 声明文件</span><br><span class="line">- react-dom // react dom的操作包</span><br><span class="line">- @types/react-dom </span><br><span class="line">- react-router-dom // react路由包</span><br><span class="line">- @types/react-router-dom</span><br><span class="line">- react-redux</span><br><span class="line">- @types/react-redux</span><br><span class="line">- redux-thunk  // 中间件</span><br><span class="line">- @types/redux-logger</span><br><span class="line">- redux-logger // 中间件</span><br><span class="line">- connected-react-router</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment">## 执行安装依赖包</span></span><br><span class="line"></span><br><span class="line">npm i react react-dom @types/react @types/react-dom react-router-dom @types/react-router-dom react-redux @types/react-redux redux-thunk redux-logger @types/redux-logger connected-react-router -S</span><br></pre></td></tr></table></figure>
<p><strong>webpack相关</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- webpack // webpack的核心包</span><br><span class="line">- webpack-cli // webapck的工具包</span><br><span class="line">- webpack-dev-server // webpack的开发服务</span><br><span class="line">- html-webpack-plugin // webpack的插件，可以生成index.html文件</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这里的<code>-D</code>相当于<code>--save-dev</code>的缩写，下载开发环境的依赖包</p>
</blockquote>
<p><strong>typescript相关</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- typescript // ts的核心包</span><br><span class="line">- ts-loader // 把ts编译成指定语法比如es5 es6等的工具，有了它，基本不需要babel了，因为它会把我们的代码编译成es5</span><br><span class="line">- source-map-loader // 用于开发环境中调试ts代码</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm i typescript ts-loader source-map-loader -D</span><br></pre></td></tr></table></figure>
<ul>
<li>从上面可以看出，基本都是模块和声明文件都是一对对出现的，有一些不是一对对出现，就是因为都集成到一起去了</li>
<li>声明文件可以在<code>node_modules/@types/xx/xx</code>中找到</li>
</ul>
<h3 id="1-3-Typescript-config配置"><a href="#1-3-Typescript-config配置" class="headerlink" title="1.3 Typescript config配置"></a>1.3 Typescript config配置</h3><blockquote>
<p>首先我们要生成一个<code>tsconfig.json</code>来告诉<code>ts-loader</code>怎样去编译这个<code>ts</code>代码</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">tsc --init</span><br></pre></td></tr></table></figure>
<blockquote>
<p>会在项目中生成了一个<code>tsconfig.json</code>文件，接下来进入这个文件，来修改相关配置</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// tsconfig.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">// 编译选项</span></span><br><span class="line">  <span class="string">"compilerOptions"</span>: &#123;</span><br><span class="line">    <span class="string">"target"</span>: <span class="string">"es5"</span>, <span class="comment">// 编译成es5语法</span></span><br><span class="line">    <span class="string">"module"</span>: <span class="string">"commonjs"</span>, <span class="comment">// 模块的类型</span></span><br><span class="line">    <span class="string">"outDir"</span>: <span class="string">"./dist"</span>, <span class="comment">// 编译后的文件目录</span></span><br><span class="line">    <span class="string">"sourceMap"</span>: <span class="literal">true</span>, <span class="comment">// 生成sourceMap方便我们在开发过程中调试</span></span><br><span class="line">    <span class="string">"noImplicitAny"</span>: <span class="literal">true</span>, <span class="comment">// 每个变量都要标明类型</span></span><br><span class="line">    <span class="string">"jsx"</span>: <span class="string">"react"</span>, <span class="comment">// jsx的版本,使用这个就不需要额外使用babel了，会编译成React.createElement</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 为了加快整个编译过程，我们指定相应的路径</span></span><br><span class="line">  <span class="string">"include"</span>: [</span><br><span class="line">    <span class="string">"./src/**/*"</span></span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="1-4-webpack配置"><a href="#1-4-webpack配置" class="headerlink" title="1.4 webpack配置"></a>1.4 webpack配置</h3><blockquote>
<p>在<code>./src/</code>下创建一个<code>index.html</code>文件，并且添加<code>&lt;div id=&#39;app&#39;&gt;&lt;/div&gt;</code>标签</p>
</blockquote>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">title</span>&gt;</span>Document<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">'app'</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>在<code>./</code>下创建一个<code>webpack</code>配置文件<code>webpack.config.js</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./webpack.config.js</span></span><br><span class="line"><span class="comment">// 引入webpack</span></span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">"webpack"</span>);</span><br><span class="line"><span class="comment">// 引入webpack插件 生成index.html文件</span></span><br><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">"html-webpack-plugin"</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">"path"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把模块导出</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 以前是jsx，因为我们用typescript写，所以这里后缀是tsx</span></span><br><span class="line">  entry:<span class="string">"./src/index.tsx"</span>,</span><br><span class="line">  <span class="comment">// 指定模式为开发模式</span></span><br><span class="line">  mode:<span class="string">"development"</span>,</span><br><span class="line">  <span class="comment">// 输出配置</span></span><br><span class="line">  output:&#123;</span><br><span class="line">    <span class="comment">// 输出目录为当前目录下的dist目录</span></span><br><span class="line">    path:path.resolve(__dirname,<span class="string">'dist'</span>),</span><br><span class="line">    <span class="comment">// 输出文件名</span></span><br><span class="line">    filename:<span class="string">"index.js"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 为了方便调试，还要配置一下调试工具</span></span><br><span class="line">  devtool:<span class="string">"source-map"</span>,</span><br><span class="line">  <span class="comment">// 解析路径，查找模块的时候使用</span></span><br><span class="line">  resolve:&#123;</span><br><span class="line">    <span class="comment">// 一般写模块不会写后缀，在这里配置好相应的后缀，那么当我们不写后缀时，会按照这个后缀优先查找</span></span><br><span class="line">    extensions:[<span class="string">".ts"</span>,<span class="string">'.tsx'</span>,<span class="string">'.js'</span>,<span class="string">'.json'</span>]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 解析处理模块的转化</span></span><br><span class="line">  <span class="built_in">module</span>:&#123;</span><br><span class="line">    <span class="comment">// 遵循的规则</span></span><br><span class="line">    rules:[</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="comment">// 如果这个模块是.ts或者.tsx，则会使用ts-loader把代码转成es5</span></span><br><span class="line">        test:<span class="regexp">/\.tsx?$/</span>,</span><br><span class="line">        loader:<span class="string">"ts-loader"</span></span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="comment">// 使用sourcemap调试</span></span><br><span class="line">        <span class="comment">// enforce:pre表示这个loader要在别的loader执行前执行</span></span><br><span class="line">        enforce:<span class="string">"pre"</span>,</span><br><span class="line">        test:<span class="regexp">/\.js$/</span>,</span><br><span class="line">        loader:<span class="string">"source-map-loader"</span></span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 插件的配置</span></span><br><span class="line">  plugins:[</span><br><span class="line">    <span class="comment">// 这个插件是生成index.html</span></span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      <span class="comment">// 以哪个文件为模板，模板路径</span></span><br><span class="line">      template:<span class="string">"./src/index.html"</span>,</span><br><span class="line">      <span class="comment">// 编译后的文件名</span></span><br><span class="line">      filename:<span class="string">"index.html"</span></span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> webpack.HotModuleReplacementPlugin()</span><br><span class="line">  ],</span><br><span class="line">  <span class="comment">// 开发环境服务配置</span></span><br><span class="line">  devServer:&#123;</span><br><span class="line">    <span class="comment">// 启动热更新,当模块、组件有变化，不会刷新整个页面，而是局部刷新</span></span><br><span class="line">    <span class="comment">// 需要和插件webpack.HotModuleReplacementPlugin配合使用</span></span><br><span class="line">    hot:<span class="literal">true</span>, </span><br><span class="line">    <span class="comment">// 静态资源目录</span></span><br><span class="line">    contentBase:path.resolve(__dirname,<span class="string">'dist'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>那么我们怎么运行这个<code>webpack.config.js</code>呢？这就需要我们在<code>package.json</code>配置一下脚本</p>
</blockquote>
<ul>
<li>在<code>package.json</code>里的<code>script</code>，添加<code>build</code>和<code>dev</code>的配置</li>
</ul>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"ts_react_demo"</span>,</span><br><span class="line">  <span class="attr">"version"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line">  <span class="attr">"description"</span>: <span class="string">""</span>,</span><br><span class="line">  <span class="attr">"main"</span>: <span class="string">"index.js"</span>,</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"build"</span>: <span class="string">"webpack"</span>,</span><br><span class="line">    <span class="attr">"dev"</span>:<span class="string">"webpack-dev-server"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">"keywords"</span>: [],</span><br><span class="line">  <span class="attr">"author"</span>: <span class="string">""</span>,</span><br><span class="line">  <span class="attr">"license"</span>: <span class="string">"ISC"</span>,</span><br><span class="line">  <span class="attr">"dependencies"</span>: &#123;</span><br><span class="line">    <span class="attr">"@types/react"</span>: <span class="string">"^16.7.13"</span>,</span><br><span class="line">    <span class="attr">"@types/react-dom"</span>: <span class="string">"^16.0.11"</span>,</span><br><span class="line">    <span class="attr">"@types/react-redux"</span>: <span class="string">"^6.0.10"</span>,</span><br><span class="line">    <span class="attr">"@types/react-router-dom"</span>: <span class="string">"^4.3.1"</span>,</span><br><span class="line">    <span class="attr">"connected-react-router"</span>: <span class="string">"^5.0.1"</span>,</span><br><span class="line">    <span class="attr">"react"</span>: <span class="string">"^16.6.3"</span>,</span><br><span class="line">    <span class="attr">"react-dom"</span>: <span class="string">"^16.6.3"</span>,</span><br><span class="line">    <span class="attr">"react-redux"</span>: <span class="string">"^6.0.0"</span>,</span><br><span class="line">    <span class="attr">"react-router-dom"</span>: <span class="string">"^4.3.1"</span>,</span><br><span class="line">    <span class="attr">"redux-logger"</span>: <span class="string">"^3.0.6"</span>,</span><br><span class="line">    <span class="attr">"redux-thunk"</span>: <span class="string">"^2.3.0"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">"devDependencies"</span>: &#123;</span><br><span class="line">    <span class="attr">"html-webpack-plugin"</span>: <span class="string">"^3.2.0"</span>,</span><br><span class="line">    <span class="attr">"source-map-loader"</span>: <span class="string">"^0.2.4"</span>,</span><br><span class="line">    <span class="attr">"ts-loader"</span>: <span class="string">"^5.3.1"</span>,</span><br><span class="line">    <span class="attr">"typescript"</span>: <span class="string">"^3.2.1"</span>,</span><br><span class="line">    <span class="attr">"webpack"</span>: <span class="string">"^4.27.1"</span>,</span><br><span class="line">    <span class="attr">"webpack-cli"</span>: <span class="string">"^3.1.2"</span>,</span><br><span class="line">    <span class="attr">"webpack-dev-server"</span>: <span class="string">"^3.1.10"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>因为入口文件是<code>index.tsx</code>，那么我们在<code>./src/</code>下创建一个<code>index.tsx</code>，并且在里面写入一段代码，看看<code>webpack</code>是否能够正常编译</li>
<li>因为我们在<code>webpack.config.js</code>中<code>entry</code>设置的入口文件是<code>index.tsx</code>，并且在<code>module</code>中的<code>rules</code>会识别到<code>.tsx</code>格式的文件，然后执行相应的<code>ts-loader</code></li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// ./src/index.tsx</span><br><span class="line">console.log(&quot;hello poetries&quot;)</span><br></pre></td></tr></table></figure>
<ul>
<li>接下来我们<code>npm run build</code>一下，看看能不能正常编译</li>
<li>编译成功，我们可以看看<code>./dist/</code>下生成了<code>index.html index.js index.js.map</code>三个文件</li>
<li>那么我们在开发过程中，不会每次都<code>npm run build</code>来看修改的结果，那么我们平时开发过程中可以使用<code>npm run dev</code>。这样就启动成功了一个<code>http://localhost:8080/</code>的服务了。</li>
<li>接下来我们看看热更新是否配置正常，在<code>./src/index.tsx</code>中新增一个<code>console.log(&#39;hello poetries&#39;)</code>，我们发现浏览器的控制台会自动打印出这一个输出，说明配置正常了</li>
</ul>
<h2 id="二、React组件"><a href="#二、React组件" class="headerlink" title="二、React组件"></a>二、React组件</h2><h3 id="2-1-写一个计数器组件"><a href="#2-1-写一个计数器组件" class="headerlink" title="2.1 写一个计数器组件"></a>2.1 写一个计数器组件</h3><blockquote>
<p>首先我们在<code>./src/</code>下创建一个文件夹<code>components</code>，然后在<code>./src/components/</code>下创建文件<code>Counter.tsx</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/components/Counter.tsx</span></span><br><span class="line"><span class="comment">// import React from "react"; // 之前的写法</span></span><br><span class="line"><span class="comment">// 在ts中引入的写法</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">CounterComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>&#123;</span><br><span class="line">  <span class="comment">// 状态state</span></span><br><span class="line">  state = &#123;</span><br><span class="line">    number:<span class="number">0</span></span><br><span class="line">  &#125;</span><br><span class="line">  render()&#123;</span><br><span class="line">    <span class="keyword">return</span>(</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;p&gt;&#123;<span class="keyword">this</span>.state.number&#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">        &lt;button onClick=&#123;()=&gt;this.setState(&#123;number:this.state.number + 1&#125;)&#125;&gt;+&lt;/</span>button&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们发现，其实除了引入<code>import * as React from &quot;react&quot;</code>以外，其余的和之前的写法没什么不同。</p>
</blockquote>
<ul>
<li>接下来我们到<code>./src/index.tsx</code>中把这个组件导进来</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/index.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> ReactDom <span class="keyword">from</span> <span class="string">"react-dom"</span>;</span><br><span class="line"><span class="keyword">import</span> CounterComponent <span class="keyword">from</span> <span class="string">"./components/Counter"</span>;</span><br><span class="line"><span class="comment">// 把我们的CounterComponent组件渲染到id为app的标签内</span></span><br><span class="line">ReactDom.render(<span class="xml"><span class="tag">&lt;<span class="name">CounterComponent</span> /&gt;</span></span>,<span class="built_in">document</span>.getElementById(<span class="string">"app"</span>))</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这样我们就把这个组件引进来了，接下来我们看下是否能够成功跑起来</p>
</blockquote>
<p><img src="https://upload-images.jianshu.io/upload_images/1480597-86ad083c5149a72e.png" alt="image.png"></p>
<blockquote>
<p>到目前为止，感觉用<code>ts</code>写<code>react</code>还是跟以前差不多，没什么区别，要记住，<code>ts</code>最大的特点就是类型检查，可以检验属性的状态类型</p>
</blockquote>
<p>假设我们需要在<code>./src/index.tsx</code>中给<code>&lt;CounterComponent /&gt;</code>传一个属性<code>name</code>，而<code>CounterComponent</code>组件需要对这个传入的<code>name</code>进行类型校验，比如说只允许传字符串</p>
<ul>
<li><code>./src/index.tsx</code>中修改一下</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">ReactDom.render(&lt;CounterComponent name=&quot;poetries&quot; /&gt;,document.getElementById(&quot;app&quot;))</span><br></pre></td></tr></table></figure>
<blockquote>
<p>然后需要在<code>./src/components/Counter.tsx</code>中写一个接口来对这个<code>name</code>进行类型校验</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// import React from "react"; // 之前的写法</span></span><br><span class="line"><span class="comment">// 在ts中引入的写法</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写一个接口对name进行类型校验</span></span><br><span class="line"><span class="comment">// 如果我们不写校验的话，在外部传name进来会报错的</span></span><br><span class="line">interface IProps&#123;</span><br><span class="line">    name:string,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们还可以用接口约束state的状态</span></span><br><span class="line">interface IState&#123;</span><br><span class="line">    number: number</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把接口约束的规则写在这里</span></span><br><span class="line"><span class="comment">// 如果传入的name不符合类型会报错</span></span><br><span class="line"><span class="comment">// 如果state的number属性不符合类型也会报错</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">CounterComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span>&lt;<span class="title">IProps</span>,<span class="title">IState</span>&gt;</span>&#123;</span><br><span class="line">  <span class="comment">// 状态state</span></span><br><span class="line">  state = &#123;</span><br><span class="line">    number:<span class="number">0</span></span><br><span class="line">  &#125;</span><br><span class="line">  render()&#123;</span><br><span class="line">    <span class="keyword">return</span>(</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">        &lt;p&gt;&#123;<span class="keyword">this</span>.state.number&#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">        &lt;p&gt;&#123;this.props.name&#125;&lt;/</span>p&gt;</span><br><span class="line">        &lt;button onClick=&#123;()=&gt;<span class="keyword">this</span>.setState(&#123;<span class="attr">number</span>:<span class="keyword">this</span>.state.number + <span class="number">1</span>&#125;)&#125;&gt;+&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>div&gt;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-2-结合Redux使用"><a href="#2-2-结合Redux使用" class="headerlink" title="2.2 结合Redux使用"></a>2.2 结合Redux使用</h3><h4 id="2-2-1-基础使用"><a href="#2-2-1-基础使用" class="headerlink" title="2.2.1 基础使用"></a>2.2.1 基础使用</h4><ul>
<li>上面<code>state</code>中的<code>number</code>就不放在组件里了，我们放到<code>redux</code>中，接下来我们使用<code>redux</code></li>
<li>首先在<code>./src/</code>创建<code>store</code>目录，然后在<code>./src/store/</code>创建一个文件<code>index.tsx</code></li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// .src/store/index.tsx</span></span><br><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">"redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入reducers</span></span><br><span class="line"><span class="keyword">import</span> reducers <span class="keyword">from</span> <span class="string">"./reducers"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接着创建仓库</span></span><br><span class="line"><span class="keyword">let</span> store = createStore(reducers);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 导出store仓库</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> store;</span><br></pre></td></tr></table></figure>
<ul>
<li>然后我们需要创建一个<code>reducers</code>，在<code>./src/store/</code>创建一个目录<code>reducers</code>，该目录下再创建一个文件<code>index.tsx</code>。</li>
<li>但是我们还需要对<code>reducers</code>中的函数参数进行类型校验，而且这个类型校验很多地方需要复用，那么我们需要把这个类型校验单独抽离出一个文件。</li>
<li>那么我们需要在<code>./src/</code>下创建一个<code>types</code>目录，该目录下创建一个文件<code>index.tsx</code></li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/types/index.tsx</span></span><br><span class="line"><span class="comment">// 导出一个接口</span></span><br><span class="line"><span class="keyword">export</span> interface Store&#123;</span><br><span class="line">  <span class="comment">// 我们需要约束的属性和类型</span></span><br><span class="line">  number:number</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>回到<code>./src/store/reducers/index.tsx</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 导入类型校验的接口</span></span><br><span class="line"><span class="comment">// 用来约束state的</span></span><br><span class="line"><span class="keyword">import</span> &#123; Store &#125; <span class="keyword">from</span> <span class="string">"../../types/index"</span></span><br><span class="line"><span class="comment">// 我们需要给number赋予默认值</span></span><br><span class="line"><span class="keyword">let</span> initState:Store = &#123; <span class="attr">number</span>:<span class="number">0</span> &#125;</span><br><span class="line"><span class="comment">// 把接口写在state:Store</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params">state:Store=initState,action</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 拿到老的状态state和新的状态action</span></span><br><span class="line">  <span class="comment">// action是一个动作行为，而这个动作行为，在计数器中是具备 加 或 减 两个功能</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>上面这段代码暂时先这样，因为需要用到<code>action</code>，我们现在去配置一下<code>action</code>相关的，首先我们在<code>./src/store</code>下创建一个<code>actions</code>目录，并且在该目录下创建文件<code>counter.tsx</code></li>
<li>因为配置<code>./src/store/actions/counter.tsx</code>会用到动作类型，而这个动作类型是属于常量，为了更加规范我们的代码，我们在<code>./src/store/</code>下创建一个<code>action-types.tsx</code>，里面写相应常量</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/store/action-types.tsx</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> ADD = <span class="string">"ADD"</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> SUBTRACT = <span class="string">"SUBTRACT"</span>;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>回到<code>./src/store/actions/counter.tsx</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/store/actions/counter.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> types <span class="keyword">from</span> <span class="string">"../action-types"</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  add()&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.ADD&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  subtract()&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.SUBTRACT&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们可以想一下，上面<code>return { type:types.ADD }</code>实际上是返回一个<code>action</code>对象，将来使用的时候，是会传到<code>./src/store/reducers/index.tsx</code>的<code>action</code>中，那么我们怎么定义这个<code>action</code>的结构呢？</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/store/actions/counter.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> types <span class="keyword">from</span> <span class="string">"../action-types"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义两个接口，分别约束add和subtract的type类型</span></span><br><span class="line"><span class="keyword">export</span> interface Add&#123;</span><br><span class="line">  type:<span class="keyword">typeof</span> types.ADD</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> interface Subtract&#123;</span><br><span class="line">  type:<span class="keyword">typeof</span> types.SUBTRACT</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再导出一个type</span></span><br><span class="line"><span class="comment">// type是用来给类型起别名的</span></span><br><span class="line"><span class="comment">// 这个actions里是一个对象，会有很多函数，每个函数都会返回一个action</span></span><br><span class="line"><span class="comment">// 而 ./store/reducers/index.tsx中的action会是下面某一个函数的返回值</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> type Action = Add | Subtract</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把上面定义好的接口作用于下面</span></span><br><span class="line"><span class="comment">// 约束返回值的类型</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  add():Add&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.ADD&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  subtract():Subtract&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.SUBTRACT&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>接着我们回到<code>./store/reducers/index.tsx</code></p>
</blockquote>
<p>经过上面一系列的配置，我们可以给<code>action</code>使用相应的接口约束了并且根据不同的<code>action</code>动作行为来进行不同的处理</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./store/reducers/index.tsx</span></span><br><span class="line"><span class="comment">// 导入类型校验的接口</span></span><br><span class="line"><span class="comment">// 用来约束state的</span></span><br><span class="line"><span class="keyword">import</span> &#123; Store &#125; <span class="keyword">from</span> <span class="string">"../../types/index"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 导入约束action的接口</span></span><br><span class="line"><span class="keyword">import</span> &#123; Action &#125; <span class="keyword">from</span> <span class="string">"../actions/counter"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入action动作行为的常量</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> types <span class="keyword">from</span> <span class="string">"../action-types"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们需要给number赋予默认值</span></span><br><span class="line"><span class="keyword">let</span> initState:Store = &#123; <span class="attr">number</span>:<span class="number">0</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把接口写在state:Store</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params">state:Store=initState,action:Action</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 拿到老的状态state和新的状态action</span></span><br><span class="line">  <span class="comment">// action是一个动作行为，而这个动作行为，在计数器中是具备 加 或 减 两个功能</span></span><br><span class="line">  <span class="comment">// 判断action的行为类型</span></span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> types.ADD:</span><br><span class="line">        <span class="comment">// 当action动作行为是ADD的时候，给number加1</span></span><br><span class="line">        <span class="keyword">return</span> &#123; <span class="attr">number</span>:state.number + <span class="number">1</span> &#125;</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> types.SUBTRACT:</span><br><span class="line">        <span class="comment">// 当action动作行为是SUBTRACT的时候，给number减1</span></span><br><span class="line">        <span class="keyword">return</span> &#123; <span class="attr">number</span>:state.number - <span class="number">1</span> &#125;</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">        <span class="comment">// 当没有匹配到则返回原本的state</span></span><br><span class="line">        <span class="keyword">return</span> state</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>接下来，我们怎么样把组件和仓库建立起关系呢</p>
</blockquote>
<p>首先进入<code>./src/index.tsx</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/index.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> ReactDom <span class="keyword">from</span> <span class="string">"react-dom"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入redux这个库的Provider组件</span></span><br><span class="line"><span class="keyword">import</span> &#123; Provider &#125; <span class="keyword">from</span> <span class="string">"react-redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入仓库</span></span><br><span class="line"><span class="keyword">import</span> store <span class="keyword">from</span> <span class="string">'./store'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> CounterComponent <span class="keyword">from</span> <span class="string">"./components/Counter"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用Provider包裹CounterComponent组件</span></span><br><span class="line"><span class="comment">// 并且把store传给Provider</span></span><br><span class="line"><span class="comment">// 这样Provider可以向它的子组件提供store</span></span><br><span class="line">ReactDom.render((</span><br><span class="line">  &lt;Provider store=&#123;store&#125;&gt;</span><br><span class="line">    &lt;CounterComponent name=<span class="string">"poetries"</span>/&gt;</span><br><span class="line">  &lt;<span class="regexp">/Provider&gt;</span></span><br><span class="line"><span class="regexp">),document.getElementById("app"))</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们到组件内部建立连接，<code>./src/components/Counter.tsx</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// import React from "react"; // 之前的写法</span></span><br><span class="line"><span class="comment">// 在ts中引入的写法</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入connect，让组件和仓库建立连接</span></span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">"react-redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入actions，用于传给connect</span></span><br><span class="line"><span class="keyword">import</span> actions <span class="keyword">from</span> <span class="string">"../store/actions/counter"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入接口约束</span></span><br><span class="line"><span class="keyword">import</span> &#123; Store &#125; <span class="keyword">from</span> <span class="string">"../types"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接口约束</span></span><br><span class="line">interface IProps&#123;</span><br><span class="line">  number:number,</span><br><span class="line"></span><br><span class="line">  name:string, <span class="comment">//如果我们不写校验的话，在外部传name进来会报错的</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// add是一个函数</span></span><br><span class="line">  add:any,</span><br><span class="line"></span><br><span class="line">  <span class="comment">// subtract是一个函数</span></span><br><span class="line">  subtract:any</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们还可以用接口约束state的状态</span></span><br><span class="line">interface IState&#123;</span><br><span class="line">    number: number</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把接口约束的规则写在这里</span></span><br><span class="line"><span class="comment">// 如果传入的name不符合类型会报错</span></span><br><span class="line"><span class="comment">// 如果state的number属性不符合类型也会报错</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CounterComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span>&lt;<span class="title">IProps</span>,<span class="title">IState</span>&gt;</span>&#123;</span><br><span class="line">  <span class="comment">// 状态state</span></span><br><span class="line">  state = &#123;</span><br><span class="line">    number:<span class="number">0</span></span><br><span class="line">  &#125;</span><br><span class="line">  render()&#123;</span><br><span class="line">    <span class="comment">// 利用解构赋值取出</span></span><br><span class="line">    <span class="comment">// 这里比如和IProps保持一致，不对应则会报错，因为接口约束了必须这样</span></span><br><span class="line">    <span class="keyword">let</span> &#123; number,add,subtract &#125; = <span class="keyword">this</span>.props</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>(</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">            &lt;h1&gt;&#123;<span class="keyword">this</span>.props.name&#125;&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">            &lt;button onClick=&#123;add&#125;&gt;+&lt;/</span>button&gt;<span class="xml"><span class="tag">&lt;<span class="name">br</span> /&gt;</span></span></span><br><span class="line">            &lt;button onClick=&#123;subtract&#125;&gt;-&lt;<span class="regexp">/button&gt;  </span></span><br><span class="line"><span class="regexp">            &lt;p&gt;&#123;number&#125;&lt;/</span>p&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 这个connect需要执行两次，第二次需要我们把这个组件CounterComponent传进去</span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ connect第一次执行，需要两个参数，</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 需要传给connect的函数</span></span><br><span class="line"><span class="regexp">let mapStateToProps = function (state:Store) &#123;</span></span><br><span class="line"><span class="regexp">    return state</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">  </span></span><br><span class="line"><span class="regexp">export default connect(</span></span><br><span class="line"><span class="regexp">    mapStateToProps,</span></span><br><span class="line"><span class="regexp">    actions</span></span><br><span class="line"><span class="regexp">)(CounterComponent);</span></span><br></pre></td></tr></table></figure>
<p>这时候看到成功执行了</p>
<p><img src="https://upload-images.jianshu.io/upload_images/1480597-32fc82d43dadb063.png" alt="image.png"></p>
<ul>
<li>其实搞来搞去，跟原来的写法差不多，主要就是<code>ts</code>会进行类型检查。</li>
<li>如果对<code>number</code>进行异步修改，该怎么处理？这就需要我们用到<code>redux-thunk</code></li>
</ul>
<blockquote>
<p>接着我们回到<code>./src/store/index.tsx</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 需要使用到thunk，所以引入中间件applyMiddleware</span></span><br><span class="line"><span class="keyword">import</span> &#123; createStore, applyMiddleware &#125; <span class="keyword">from</span> <span class="string">"redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入reducers</span></span><br><span class="line"><span class="keyword">import</span> reducers <span class="keyword">from</span> <span class="string">"./reducers"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入redux-thunk，处理异步</span></span><br><span class="line"><span class="comment">// 现在主流处理异步的是saga和thunk</span></span><br><span class="line"><span class="keyword">import</span> thunk <span class="keyword">from</span> <span class="string">"redux-thunk"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入日志</span></span><br><span class="line"><span class="keyword">import</span> logger <span class="keyword">from</span> <span class="string">"redux-logger"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接着创建仓库和中间件</span></span><br><span class="line"><span class="keyword">let</span> store = createStore(reducers, applyMiddleware(thunk,logger));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 导出store仓库</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> store;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>接着我们回来<code>./src/store/actions</code>，新增一个异步的动作行为</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/store/actions/counter.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> types <span class="keyword">from</span> <span class="string">"../action-types"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义两个接口，分别约束add和subtract的type类型</span></span><br><span class="line"><span class="keyword">export</span> interface Add&#123;</span><br><span class="line">  type:<span class="keyword">typeof</span> types.ADD</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> interface Subtract&#123;</span><br><span class="line">  type:<span class="keyword">typeof</span> types.SUBTRACT</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 再导出一个type</span></span><br><span class="line"><span class="comment">// type是用来给类型起别名的</span></span><br><span class="line"><span class="comment">// 这个actions里是一个对象，会有很多函数，每个函数都会返回一个action</span></span><br><span class="line"><span class="comment">// 而 ./store/reducers/index.tsx中的action会是下面某一个函数的返回值</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> type Action = Add | Subtract</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把上面定义好的接口作用于下面</span></span><br><span class="line"><span class="comment">// 约束返回值的类型</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  add():Add&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.ADD&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  subtract():Subtract&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.SUBTRACT&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 一秒后才执行这个行为</span></span><br><span class="line">  <span class="comment">// ++</span></span><br><span class="line">  addAsync():any&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">dispatch:any,getState:any</span>) </span>&#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="comment">// 当1秒过后，会执行dispatch，派发出去，然后改变仓库的状态</span></span><br><span class="line">        dispatch(&#123;<span class="attr">type</span>:types.ADD&#125;)</span><br><span class="line">      &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>到<code>./src/components/Counter.tsx</code>组件内，使用这个异步</p>
</blockquote>
<h4 id="2-2-2-合并reducers"><a href="#2-2-2-合并reducers" class="headerlink" title="2.2.2 合并reducers"></a>2.2.2 合并reducers</h4><blockquote>
<p>假如我们的项目里面，有两个计数器，而且它俩是完全没有关系的，状态也是完全独立的，这个时候就需要用到合并<code>reducers</code>了</p>
</blockquote>
<ul>
<li>首先我们新增<code>action</code>的动作行为类型，在<code>./src/store/action-types.tsx</code></li>
<li>然后修改接口文件，<code>./src/types/index.tsx</code></li>
<li>然后把<code>./src/store/actions/counter.tsx</code>文件拷贝在当前目录并且修改名称为<code>counter2.tsx</code></li>
<li>然后把<code>./src/store/reduces/index.tsx</code>拷贝并且改名为<code>counter.tsx</code>和<code>counter2.tsx</code></li>
</ul>
<blockquote>
<p>我们多个<code>reducer</code>是通过<code>combineReducers</code>方法，进行合并的，因为我们一个项目当中肯定是存在非常多个<code>reducer</code>，所以统一在这里处理。</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/store/reducers/index.tsc</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入合并方法</span></span><br><span class="line"><span class="keyword">import</span> &#123; combineReducers &#125; <span class="keyword">from</span> <span class="string">"redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入需要合并的reducer</span></span><br><span class="line"><span class="keyword">import</span> counter <span class="keyword">from</span> <span class="string">"./counter"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入需要合并的reducer</span></span><br><span class="line"><span class="keyword">import</span> counter2 <span class="keyword">from</span> <span class="string">"./counter2"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 合并</span></span><br><span class="line"><span class="keyword">let</span> reducers = combineReducers(&#123;</span><br><span class="line">  counter,</span><br><span class="line">  counter2,</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> reducers;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>最后修改组件，进入<code>./src/components/</code>,其中</p>
</blockquote>
<p>到目前为止，我们完成了<code>reducers</code>的合并了，那么我们看看效果如何，首先我们给<code>./src/index.tsc</code>添加<code>Counter2</code>组件，这样的目的是与<code>Counter</code>组件完全独立，互不影响，但是又能够最终合并到<code>readucers</code></p>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/548.png" alt></p>
<h3 id="2-3-路由"><a href="#2-3-路由" class="headerlink" title="2.3 路由"></a>2.3 路由</h3><h4 id="2-3-1-基本用法"><a href="#2-3-1-基本用法" class="headerlink" title="2.3.1 基本用法"></a>2.3.1 基本用法</h4><blockquote>
<p>首先进入<code>./src/index.tsx</code>导入我们的路由所需要的依赖包</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/index.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> ReactDom <span class="keyword">from</span> <span class="string">"react-dom"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入redux这个库的Provider组件</span></span><br><span class="line"><span class="keyword">import</span> &#123; Provider &#125; <span class="keyword">from</span> <span class="string">"react-redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入路由</span></span><br><span class="line"><span class="comment">// 路由的容器:HashRouter as Router</span></span><br><span class="line"><span class="comment">// 路由的规格:Route</span></span><br><span class="line"><span class="comment">// Link组件</span></span><br><span class="line"><span class="keyword">import</span> &#123; BrowserRouter <span class="keyword">as</span> Router,Route,Link &#125; <span class="keyword">from</span> <span class="string">"react-router-dom"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入仓库</span></span><br><span class="line"><span class="keyword">import</span> store <span class="keyword">from</span> <span class="string">'./store'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> CounterComponent <span class="keyword">from</span> <span class="string">"./components/Counter"</span>;</span><br><span class="line"><span class="keyword">import</span> CounterComponent2 <span class="keyword">from</span> <span class="string">"./components/Counter2"</span>;</span><br><span class="line"><span class="keyword">import</span> Counter <span class="keyword">from</span> <span class="string">"./components/Counter"</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Home</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>home<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用Provider包裹CounterComponent组件</span></span><br><span class="line"><span class="comment">// 并且把store传给Provider</span></span><br><span class="line"><span class="comment">// 这样Provider可以向它的子组件提供store</span></span><br><span class="line">ReactDom.render((</span><br><span class="line">  &lt;Provider store=&#123;store&#125;&gt;</span><br><span class="line">    &#123;<span class="comment">/* 路由组件 */</span>&#125;</span><br><span class="line">    &lt;Router&gt;</span><br><span class="line">      &#123;<span class="comment">/*  放两个路由规则需要在外层套个React.Fragment */</span>&#125;</span><br><span class="line">        &lt;React.Fragment&gt;</span><br><span class="line">            &#123;<span class="comment">/* 增加导航 */</span>&#125;</span><br><span class="line">            &lt;ul&gt;</span><br><span class="line">            &lt;li&gt;<span class="xml"><span class="tag">&lt;<span class="name">Link</span> <span class="attr">to</span>=<span class="string">"/"</span>&gt;</span>Home<span class="tag">&lt;/<span class="name">Link</span>&gt;</span></span>&lt;<span class="regexp">/li&gt;</span></span><br><span class="line"><span class="regexp">            &lt;li&gt;&lt;Link to="/</span>counter<span class="string">"&gt;Counter&lt;/Link&gt;&lt;/li&gt;</span></span><br><span class="line"><span class="string">            &lt;li&gt;&lt;Link to="</span>/counter2<span class="string">"&gt;Counter2&lt;/Link&gt;&lt;/li&gt;</span></span><br><span class="line"><span class="string">            &lt;/ul&gt;</span></span><br><span class="line"><span class="string">            &#123;/* 当路径为 / 时是home组件 */&#125;</span></span><br><span class="line"><span class="string">            &#123;/* 为了避免home组件一直渲染，我们可以添加属性exact */&#125;</span></span><br><span class="line"><span class="string">            &lt;Route exact path="</span>/<span class="string">" component=&#123;Home&#125;/&gt;</span></span><br><span class="line"><span class="string">            &lt;Route path="</span>/counter<span class="string">" component=&#123;CounterComponent&#125;/&gt;</span></span><br><span class="line"><span class="string">            &lt;Route path="</span>/counter2<span class="string">" component=&#123;CounterComponent2&#125; /&gt;</span></span><br><span class="line"><span class="string">        &lt;/React.Fragment&gt;</span></span><br><span class="line"><span class="string">        &lt;/Router&gt;</span></span><br><span class="line"><span class="string">  &lt;/Provider&gt;</span></span><br><span class="line"><span class="string">),document.getElementById("</span>app<span class="string">"))</span></span><br></pre></td></tr></table></figure>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/549.png" alt></p>
<blockquote>
<p>但是有个很大的问题，就是我们直接访问<code>http://localhost:8080/counter</code>会找不到路由</p>
</blockquote>
<ul>
<li>因为我们的是单页面应用，不管路由怎么变更，实际上都是访问<code>index.html</code>这个文件，所以当我们访问根路径的时候，能够正常访问，因为<code>index.html</code>文件就放在这个目录下，但是当我们通过非根路径的路由访问，则出错了，是因为我们在相应的路径没有这个文件，所以出错了</li>
<li>从这一点也可以衍生出一个实战经验，我们平时项目部署上线的时候，会出现这个问题，一般我们都是用<code>nginx</code>来把访问的路径都是指向<code>index.html</code>文件，这样就能够正常访问了。</li>
<li>那么针对目前我们这个情况，我们可以通过修改<code>webpack</code>配置，让路由不管怎么访问，都是指向我们制定的<code>index.html</code>文件。</li>
</ul>
<blockquote>
<p>进入<code>./webpack.config.js</code>，在<code>devServer</code>的配置对象下新增一些配置</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./webpack.config.js</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 开发环境服务配置</span></span><br><span class="line">  devServer:&#123;</span><br><span class="line">    <span class="comment">// 启动热更新,当模块、组件有变化，不会刷新整个页面，而是局部刷新</span></span><br><span class="line">    <span class="comment">// 需要和插件webpack.HotModuleReplacementPlugin配合使用</span></span><br><span class="line">    hot:<span class="literal">true</span>, </span><br><span class="line">    <span class="comment">// 静态资源目录</span></span><br><span class="line">    contentBase:path.resolve(__dirname,<span class="string">'dist'</span>),</span><br><span class="line">    <span class="comment">// 不管访问什么路径，都重定向到index.html</span></span><br><span class="line">    historyApiFallback:&#123;</span><br><span class="line">      index:<span class="string">"./index.html"</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>修改<code>webpack</code>配置需要重启服务，然后重启服务，看看浏览器能否正常访问<code>http://localhost:8080/counter</code></p>
</blockquote>
<h4 id="2-3-2-同步路由到redux"><a href="#2-3-2-同步路由到redux" class="headerlink" title="2.3.2 同步路由到redux"></a>2.3.2 同步路由到redux</h4><blockquote>
<p>路由的路径，如何同步到仓库当中。以前是用一个叫<code>react-router-redux</code>的库，把路由和<code>redux</code>结合到一起的，<code>react-router-redux</code>挺好用的，但是这个库不再维护了，被废弃了，所以现在推荐使用<code>connected-react-router</code>这个库，可以把路由状态映射到仓库当中</p>
</blockquote>
<blockquote>
<p>首先我们在<code>./src</code>下创建文件<code>history.tsx</code></p>
</blockquote>
<p>假设我有一个需求，就是我不通过<code>Link</code>跳转页面，而是通过编程式导航，触发一个动作，然后这个动作会派发出去，而且把路由信息放到<code>redux</code>中，供我以后查看。</p>
<blockquote>
<p>我们进入<code>./src/store/reducers/index.tsx</code></p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 引入合并方法</span></span><br><span class="line"><span class="keyword">import</span> &#123; combineReducers &#125; <span class="keyword">from</span> <span class="string">"redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入需要合并的reducer</span></span><br><span class="line"><span class="keyword">import</span> counter <span class="keyword">from</span> <span class="string">"./counter"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入需要合并的reducer</span></span><br><span class="line"><span class="keyword">import</span> counter2 <span class="keyword">from</span> <span class="string">"./counter2"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入connectRouter</span></span><br><span class="line"><span class="keyword">import</span> &#123; connectRouter &#125; <span class="keyword">from</span> <span class="string">"connected-react-router"</span>;</span><br><span class="line"><span class="keyword">import</span> history <span class="keyword">from</span> <span class="string">"../../history"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 合并</span></span><br><span class="line"><span class="keyword">let</span> reducers = combineReducers(&#123;</span><br><span class="line">  counter,</span><br><span class="line">  counter2,</span><br><span class="line">  <span class="comment">// 把history传到connectRouter函数中</span></span><br><span class="line">  router: connectRouter(history)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> reducers;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们进入<code>./src/store/index.tsx</code>来添加中间件</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 需要使用到thunk，所以引入中间件applyMiddleware</span></span><br><span class="line"><span class="keyword">import</span> &#123; createStore, applyMiddleware &#125; <span class="keyword">from</span> <span class="string">"redux"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入reducers</span></span><br><span class="line"><span class="keyword">import</span> reducers <span class="keyword">from</span> <span class="string">"./reducers"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入redux-thunk，处理异步</span></span><br><span class="line"><span class="comment">// 现在主流处理异步的是saga和thunk</span></span><br><span class="line"><span class="keyword">import</span> thunk <span class="keyword">from</span> <span class="string">"redux-thunk"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入日志</span></span><br><span class="line"><span class="keyword">import</span> logger <span class="keyword">from</span> <span class="string">"redux-logger"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入中间件</span></span><br><span class="line"><span class="keyword">import</span> &#123; routerMiddleware &#125; <span class="keyword">from</span> <span class="string">"connected-react-router"</span>;</span><br><span class="line"><span class="keyword">import</span> history <span class="keyword">from</span> <span class="string">"../history"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接着创建仓库和中间件</span></span><br><span class="line"><span class="keyword">let</span> store = createStore(reducers, applyMiddleware(routerMiddleware(history),thunk,logger));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 导出store仓库</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> store;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们进入<code>./src/store/actions/counter.tsx</code>加个<code>goto</code>方法用来跳转</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ./src/store/actions/counter.tsx</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> types <span class="keyword">from</span> <span class="string">"../action-types"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引入push方法</span></span><br><span class="line"><span class="keyword">import</span> &#123; push &#125; <span class="keyword">from</span> <span class="string">"connected-react-router"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义两个接口，分别约束add和subtract的type类型</span></span><br><span class="line"><span class="keyword">export</span> interface Add&#123;</span><br><span class="line">  type:<span class="keyword">typeof</span> types.ADD</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> interface Subtract&#123;</span><br><span class="line">  type:<span class="keyword">typeof</span> types.SUBTRACT</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 再导出一个type</span></span><br><span class="line"><span class="comment">// type是用来给类型起别名的</span></span><br><span class="line"><span class="comment">// 这个actions里是一个对象，会有很多函数，每个函数都会返回一个action</span></span><br><span class="line"><span class="comment">// 而 ./store/reducers/index.tsx中的action会是下面某一个函数的返回值</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> type Action = Add | Subtract</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把上面定义好的接口作用于下面</span></span><br><span class="line"><span class="comment">// 约束返回值的类型</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  add():Add&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.ADD&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  subtract():Subtract&#123;</span><br><span class="line">    <span class="comment">// 需要返回一个action对象</span></span><br><span class="line">    <span class="comment">// type为动作的类型</span></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">type</span>: types.SUBTRACT&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 一秒后才执行这个行为</span></span><br><span class="line">  addAsync():any&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">dispatch:any,getState:any</span>) </span>&#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="comment">// 当1秒过后，会执行dispatch，派发出去，然后改变仓库的状态</span></span><br><span class="line">        dispatch(&#123;<span class="attr">type</span>:types.ADD&#125;)</span><br><span class="line">      &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  goto(path:string)&#123;</span><br><span class="line">    <span class="comment">// 派发一个动作</span></span><br><span class="line">    <span class="comment">// 这个push是connected-react-router里的一个方法</span></span><br><span class="line">    <span class="comment">// 返回一个跳转路径的action</span></span><br><span class="line">    <span class="keyword">return</span> push(path)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们进入<code>./src/components/Counter.tsx</code>中加个按钮，当我点击按钮的时候，会向仓库派发<code>action</code>，仓库的<code>action</code>里有中间件，会把我们这个请求拦截到，然后跳转</p>
</blockquote>

      </div>
    
  </div>

</article>

<button class="assist-btn2 circle" id="assist_btn2" title="点亮屏幕" style="left: 27px; top: 152px;">
  <i class="iconfont" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>
<button class="assist-btn1 circle" id="assist_btn1" title="关闭屏幕亮度" style="left: 27px; top: 152px;">
  <i class="iconfont toc-title" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>


<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>	

<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
<script>
  const btw = new BTWPlugin();
  btw.init({
    id: "container",
    blogId: "22699-1592137983091-414",
    name: "前端进阶之旅",
    qrcode: "https://poetries1.gitee.io/img-repo/2020/06/qrcode.jpg",
    keyword: "3a3b3c",
  });
</script>

<script type="text/javascript">

// white theme
var body = {color: "#555", background: "#000"};
var a_tag = {color: "#222"};
var header = { background: "#222"};
var logo_line_i = {background: "#222"};
// var post_code = {background: "#eee", color: "#222"};

function switch_theme() {
 $("body").css(body);
 $("a:not('.links-of-author-item a, .site-state-item a, .site-state-posts a, .feed-link a, .motion-element a, .post-tags a, .show-commit-cls a, #donate_board a')").css(a_tag);
 $(".header, .footer").css(header);
 $(".logo-line-before i, .logo-line-after i").css(logo_line_i);
 //$(".post code").css(post_code);
 $("#idhyt-surprise-ball #idhyt-surprise-ball-animation .drag").css(a_tag);
 $(".post-title-link, .posts-expand .post-meta, .post-comments-count, .disqus-comment-count, .post-category a, .post-nav-next a, .post-nav-item a").css(a_tag);
 
 // $("code").css({color: '#c5c8c6', background: '#1d1f21'});
 //$("#assist_btn1").hide(1500);
}

$(function () {
$("#assist_btn2").css("display","none");
 $("#assist_btn1").click(function() {
     switch_theme();
$("div#toc.toc-article").css({
 "background":"#eaeaea",
 "opacity":1
});
$(".toc-article ol").show();
$("#toc.toc-article .toc-title").css("color","#a98602");
$("#assist_btn1").css("display","none");
$("#assist_btn2").css("display","block");
 });
$("#assist_btn2").click(function() {
$("#assist_btn2").css("display","none");
$("#assist_btn1").css("display","block");
$("body").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
     $(".header, .footer").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
$(".toc-article ol").toggle(1000);
 });
});


//背景随机

var Y, O, E, L, B, C, T, z, N, S, A, I;
!function() {
var e = function() {
for (O.clearRect(0, 0, L, B), T = [{
x: 0,
y: .7 * B + C
}, {
x: 0,
y: .7 * B - C
}]; T[1].x < L + C;) t(T[0], T[1])
}, t = function(e, t) {
O.beginPath(), O.moveTo(e.x, e.y), O.lineTo(t.x, t.y);
var n = t.x + (2 * I() - .25) * C,
 r = a(t.y);
O.lineTo(n, r), O.closePath(), N -= S / -50, O.fillStyle = "#" + (127 * A(N) + 128 << 16 | 127 * A(N + S / 3) + 128 << 8 | 127 * A(N + S / 3 * 2) + 128).toString(16), O.fill(), T[0] = T[1], T[1] = {
 x: n,
 y: r
}
}, a = function n(e) {
var t = e + (2 * I() - 1.1) * C;
return t > B || t < 0 ? n(e) : t
};
Y = document.getElementById("evanyou"), O = Y.getContext("2d"), E = window.devicePixelRatio || 1, L = window.innerWidth, B = window.innerHeight, C = 90, z = Math, N = 0, S = 2 * z.PI, A = z.cos, I = z.random, Y.width = L * E, Y.height = B * E, O.scale(E, E), O.globalAlpha = .6, document.onclick = e, document.ontouchstart = e, e()
}()

   
$("#toc-eye").click(function(){
$("#toc.toc-article").toggle(1000);
});

</script>


   
  <div class="text-center donation">
    <div class="inner-donation">
      <span class="btn-donation">支持一下</span>
      <div class="donation-body">
        <div class="tip text-center">扫一扫，支持poetries</div>
        <ul>
        
          <li class="item">
            
              <span>微信扫一扫</span>
            
            <img src="/images/weixin.jpg" alt="">
          </li>
        
          <li class="item">
            
              <span>支付宝扫一扫</span>
            
            <img src="/images/zhifubao.jpg" alt="">
          </li>
        
        </ul>
      </div>
    </div>
  </div>


   
  <div class="box-prev-next clearfix">
    <a class="show pull-left" href="/2018/12/24/shell/">
        <i class="icon icon-angle-left"></i>
    </a>
    <a class="show pull-right" href="/2018/12/30/ts-summary/">
        <i class="icon icon-angle-right"></i>
    </a>
  </div>




</div>


  <a id="backTop" class="back-top">
    <i class="icon-angle-up"></i>
  </a>




  <div class="modal" id="modal">
  <span id="cover" class="cover hide"></span>
  <div id="modal-dialog" class="modal-dialog hide-dialog">
    <div class="modal-header">
      <span id="close" class="btn-close">关闭</span>
    </div>
    <hr>
    <div class="modal-body">
      <ul class="list-toolbox">
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/archives/"
              rel="noopener noreferrer"
              target="_self"
              >
              博客
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/categories/"
              rel="noopener noreferrer"
              target="_self"
              >
              分类
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/tags/"
              rel="noopener noreferrer"
              target="_self"
              >
              标签
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/search/"
              rel="noopener noreferrer"
              target="_self"
              >
              搜索
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/link/"
              rel="noopener noreferrer"
              target="_self"
              >
              友链
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/about/"
              rel="noopener noreferrer"
              target="_self"
              >
              关于
            </a>
          </li>
        
      </ul>

    </div>
  </div>
</div>



  
      <div class="fexo-comments comments-post">
    

    

    
    

    

    
    

    

<!-- Gitalk评论插件通用代码 -->
<div id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script>
const gitalk = new Gitalk({
  clientID: '5567a2c4abb858009d96',
  clientSecret: 'b9039ec056cf5c2346b3cdb63308a28c163f91e5',
  repo: 'poetries.github.io',
  owner: 'poetries',
  // 在这里设置一下截取前50个字符串, 这是因为 github 对 label 的长度有了要求, 如果超过
  // 50个字符串则会报错.
  // id: location.pathname.split('/').pop().substring(0, 49),
  id: location.pathname,
  admin: ['poetries'],
  // facebook-like distraction free mode
  distractionFreeMode: false
})
gitalk.render('gitalk-container')
</script>
<!-- Gitalk代码结束 -->



  </div>

  

  <script type="text/javascript">
  function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //IE
      script.onreadystatechange = function() {
        if (script.readyState == 'loaded' ||
          script.readyState == 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else { //Others
      script.onload = function() {
        callback();
      };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  window.onload = function() {
    loadScript('/js/bundle.js?235683', function() {
      // load success
    });
  }
</script>


  <!-- 页面点击小红心 -->
  <script type="text/javascript" src="/js/clicklove.js"></script>
 
  
</body>
</html>
